﻿
Imports System.Threading
Imports std

Public Module stopwatch
    Private ReadOnly DELAY As Int64 = 0

    Public Class stopwatchEvent
        <ThreadStatic()> Private Shared _current As stopwatchEvent
        Private Const IS_CANCELED As Int32 = 1
        Private Const IS_NOT_CANCELED As Int32 = 0
        Private _canceled As Int32 = IS_NOT_CANCELED
        Friend d As void = Nothing
        Friend ms As Int64 = 0

        Public Shared Property current() As stopwatchEvent
            Get
                Dim c As stopwatchEvent = Nothing
                c = _current
                assert(Not c Is Nothing)
                Return c
            End Get
            Friend Set(ByVal value As stopwatchEvent)
                assert(value Is Nothing OrElse _current Is Nothing)
                _current = value
            End Set
        End Property

        Public Function cancel() As Boolean
            If isdebugmode() Then
                Return Interlocked.CompareExchange(_canceled, IS_CANCELED, IS_NOT_CANCELED) = IS_NOT_CANCELED
            Else
                atomic.eva(_canceled, IS_CANCELED)
                Return True
            End If
        End Function

        Public Function [resume]() As Boolean
            If isdebugmode() Then
                Return Interlocked.CompareExchange(_canceled, IS_NOT_CANCELED, IS_CANCELED) = IS_CANCELED
            Else
                atomic.eva(_canceled, IS_NOT_CANCELED)
                Return True
            End If
        End Function

        Public Function canceled() As Boolean
            Return _canceled = IS_CANCELED
        End Function
    End Class

    Private timer As Timer = Nothing
    Private q As qless(Of stopwatchEvent) = Nothing
    Private se As singleentry

    Sub New()
        Dim intervalMs As Int64 = 0
        intervalMs = spinwait.halfTimesliceLengthMs
        DELAY = counter.registerAverageAndLastAverage("STOPWATCH_DELAY_MS")
        timer = New Timer(AddressOf elapsed, Nothing, intervalMs, intervalMs)
        q = New qless(Of stopwatchEvent)()
    End Sub

    Private Sub work(ByVal i As stopwatchEvent)
        stopwatchEvent.current() = i
        i.d()
        stopwatchEvent.current() = Nothing
    End Sub

    Private Sub work(ByVal i As Object)
        work(convertor.cast(Of stopwatchEvent)(i))
    End Sub

    Private Function pop() As stopwatchEvent
        Return q.pop()
    End Function

    Private Function size() As Int64
        Return q.size()
    End Function

    Public Function push(ByVal e As stopwatchEvent) As Boolean
        If e Is Nothing OrElse e.canceled() Then
            Return False
        Else
            q.push(e)
            Return True
        End If
    End Function

    Public Function push(ByVal waitms As Int64, ByVal d As void) As stopwatchEvent
        If d Is Nothing Then
            Return Nothing
        Else
            Dim rtn As stopwatchEvent = Nothing
            rtn = New stopwatchEvent()
            rtn.d = d
            rtn.ms = waitms + Now.milliseconds()
            If push(rtn) Then
                Return rtn
            Else
                Return Nothing
            End If
        End If
    End Function

    Private Sub elapsed(ByVal NIU As Object)
        If se.mark_in_use() Then
            Dim i As Int64 = 0
            i = size()
            Dim n As Int64 = 0
            n = Now.milliseconds()
            While i > 0
                Dim e As stopwatchEvent = Nothing
                e = pop()
                assert(Not e Is Nothing)
                If Not e.canceled() Then
                    Dim diff As Int64 = 0
                    diff = n - e.ms
                    If diff >= 0 Then
                        counter.increase(DELAY, diff)
                        resolve(Of ithreadpool)().insertJob(AddressOf work, e)
                    ElseIf Not push(e) Then
                        assert(e.canceled())
                    End If
                End If
                i -= 1
            End While
            se.release()
        End If
    End Sub
End Module
